#pragma once
#include "ScratchWindowsFramework.h"

#ifdef _WIN32
#ifdef _DEBUG
   #include <crtdbg.h>
   #undef THIS_FILE
//   static char THIS_FILE[] = __FILE__;
   #define new       new( _NORMAL_BLOCK, __FILE__, __LINE__)
   #define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#endif

namespace acme {
  namespace scratch {

    // COM classes.

    template<class T>
    class ComPtr
    {
      T* _obj;
    public:
      ComPtr():_obj(0){}
      ~ComPtr(){if(_obj)_obj->Release();}
      operator T*(){return _obj;}
      T* operator ->(){return _obj;}
      T** operator &(){return &_obj;}
      ComPtr& operator=(T* rhs){
        if(_obj)
          _obj->Release();
        if(_obj=rhs)
          _obj->AddRef();
        return *this;
      }
    };

    struct BStr
    {
      BSTR _bstr;
      BStr(LPCTSTR psz){
        _bstr = SysAllocString(psz);
      }
      ~BStr(){
        SysFreeString(_bstr);
      }
      operator BSTR(){return _bstr;}
    };

    template<class T>
    class StaticUnknown : public T
    {
      volatile ULONG _cRef;
    public: // StaticUnknown
      StaticUnknown():_cRef(0){}
      ~StaticUnknown(){}
    public: // IUnknown
      virtual ULONG STDMETHODCALLTYPE AddRef( void) override{
        return InterlockedIncrement(&_cRef);
      }
      virtual ULONG STDMETHODCALLTYPE Release( void) override{
        return InterlockedDecrement(&_cRef);
      }
    };

    template<class T>
    class DynamicUnknown : public T
    {
      volatile ULONG _cRef;
    public: // StaticUnknown
      DynamicUnknown():_cRef(1){}
    private:
      ~DynamicUnknown(){}
    public: // IUnknown
      virtual ULONG STDMETHODCALLTYPE AddRef( void) override{
        return InterlockedIncrement(&_cRef);
      }
      virtual ULONG STDMETHODCALLTYPE Release( void) override{
        ULONG result = InterlockedDecrement(&_cRef);
        if(!result)
          delete this;
        return result;
      }
    };

    template<class T>
    class DynamicRef
    {
      DynamicUnknown<T>* _obj;
    public:
      DynamicRef():_obj(new DynamicUnknown<T>())
      {
      }
//#ifdef _DEBUG
      //DynamicRef(char const* file, int line):_obj(new( _NORMAL_BLOCK, file, line) DynamicUnknown<T>()){}
//#endif
      ~DynamicRef(){
        _obj->Release();
      }
      T* operator ->(){return _obj;}
      operator T*(){return _obj;}
    };

#ifdef _DEBUG
#define DYNAMICREF(T,i,s) DynamicRef<T> i(s);//(__FILE__,__LINE__);
#else
#define DYNAMICREF(T,i) DynamicRef<T> i;
#endif

#include <OleAuto.h>

    class StandardDispatch
    {
      ComPtr<ITypeInfo> _iTypeInfo;
      ComPtr<IDispatch> _iDispatch;
    public:
      void init(INTERFACEDATA* pidata,IUnknown* punkOuter,void* pClass);
      IDispatch* dispatch(){return _iDispatch;}
    };

    class GenericDispatch : public IDispatch
    {
      ComPtr<ITypeInfo> _iTypeInfo;
      void* _pClass;
    public:
      GenericDispatch();
      ~GenericDispatch();
      void init(INTERFACEDATA* pidata,void* pClass);
    public: // IUnknown
      virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void **ppvObject) override;
      virtual ULONG STDMETHODCALLTYPE AddRef( void)=0;
      virtual ULONG STDMETHODCALLTYPE Release( void)=0;
    public: // IDispatch
      virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) override;
      virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo **ppTInfo) override;
      virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid,LPOLESTR *rgszNames,UINT cNames,LCID lcid,DISPID *rgDispId) override;
      virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr) override;
    };


#include <ExDisp.h>

    class ActiveXFrame : public IOleInPlaceFrame
    {
      ComPtr<IOleInPlaceActiveObject> _active;
    public:
      ActiveXFrame();
    protected:
      ~ActiveXFrame();
    public:
      virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void **ppvObject) override;
      //virtual ULONG STDMETHODCALLTYPE AddRef( void) override;
      //virtual ULONG STDMETHODCALLTYPE Release( void) override;
    public: // IOleWindow
      virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd)override;
      virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode)override;
    public: // IOleInPlaceUIWindow
      virtual HRESULT STDMETHODCALLTYPE GetBorder(LPRECT lprectBorder)override;
      virtual HRESULT STDMETHODCALLTYPE RequestBorderSpace(LPCBORDERWIDTHS pborderwidths)override;
      virtual HRESULT STDMETHODCALLTYPE SetBorderSpace(LPCBORDERWIDTHS pborderwidths)override;
      virtual HRESULT STDMETHODCALLTYPE SetActiveObject(IOleInPlaceActiveObject *pActiveObject,LPCOLESTR pszObjName)override;
    public: // IOleInPlaceFrame
      virtual HRESULT STDMETHODCALLTYPE InsertMenus(HMENU hmenuShared,LPOLEMENUGROUPWIDTHS lpMenuWidths)override;
      virtual HRESULT STDMETHODCALLTYPE SetMenu(HMENU hmenuShared,HOLEMENU holemenu,HWND hwndActiveObject)override;
      virtual HRESULT STDMETHODCALLTYPE RemoveMenus(HMENU hmenuShared)override;
      virtual HRESULT STDMETHODCALLTYPE SetStatusText(LPCOLESTR pszStatusText)override;
      virtual HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable)override;
      virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpmsg,WORD wID)override;
    };


    class ActiveXSite :
      public IOleClientSite,
      public IOleInPlaceSite
    {
      //volatile ULONG _cRef;
    protected:
      ComPtr<IOleObject> _iOleObject;
      ComPtr<IOleInPlaceObject> _iOleInPlaceObject;
      ComPtr<IOleInPlaceFrame> _iOleFrame;
    public: // ActiveXSite
      ActiveXSite();
      virtual ~ActiveXSite();

      void init(REFCLSID clsid,IOleInPlaceFrame* frame, REFIID riid,void** ppvObject);
      virtual void close();
      void resizeTo(int x, int y, int w, int h);
    public: // IUnknown
      virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void **ppvObject) override;
      virtual ULONG STDMETHODCALLTYPE AddRef( void)=0;
      virtual ULONG STDMETHODCALLTYPE Release( void)=0;
    public: // IOleClientSite
      virtual HRESULT STDMETHODCALLTYPE SaveObject( void) override;
      virtual HRESULT STDMETHODCALLTYPE GetMoniker( 
          DWORD dwAssign,
          DWORD dwWhichMoniker,
          IMoniker **ppmk) override;
        
      virtual HRESULT STDMETHODCALLTYPE GetContainer( 
          IOleContainer **ppContainer)override;
      virtual HRESULT STDMETHODCALLTYPE ShowObject( void) override;
      virtual HRESULT STDMETHODCALLTYPE OnShowWindow( 
          BOOL fShow) override;
      virtual HRESULT STDMETHODCALLTYPE RequestNewObjectLayout( void) override;
    public: // IOleWindow
        virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd)override;
        virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode)override;
    public: // IOleInPlaceSite
      virtual HRESULT STDMETHODCALLTYPE CanInPlaceActivate( void)override;
      virtual HRESULT STDMETHODCALLTYPE OnInPlaceActivate( void)override;
      virtual HRESULT STDMETHODCALLTYPE OnUIActivate( void)override;
      virtual HRESULT STDMETHODCALLTYPE GetWindowContext( 
        IOleInPlaceFrame **ppFrame,
        IOleInPlaceUIWindow **ppDoc,
        LPRECT lprcPosRect,
        LPRECT lprcClipRect,
        LPOLEINPLACEFRAMEINFO lpFrameInfo)override;
      virtual HRESULT STDMETHODCALLTYPE Scroll(SIZE scrollExtant)override;
      virtual HRESULT STDMETHODCALLTYPE OnUIDeactivate(BOOL fUndoable)override;
      virtual HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate( void)override;
      virtual HRESULT STDMETHODCALLTYPE DiscardUndoState( void)override;
      virtual HRESULT STDMETHODCALLTYPE DeactivateAndUndo( void)override;
      virtual HRESULT STDMETHODCALLTYPE OnPosRectChange(LPCRECT lprcPosRect)override;
    };



#include <MsHTML.h>
#include <MsHtmHst.h>

    struct WebBrowserEventsDelegate
    {
//        [id(DISPID_STATUSTEXTCHANGE), helpstring("Statusbar text changed."), helpcontext(0x0000)]
      virtual void __cdecl StatusTextChange(BSTR Text){} // 0
//        [id(DISPID_PROGRESSCHANGE), helpstring("Fired when download progress is updated."), helpcontext(0x0000)]
      virtual void __cdecl ProgressChange(long Progress, long ProgressMax){} // 1
//        [id(DISPID_COMMANDSTATECHANGE), helpstring("The enabled state of a command changed."), helpcontext(0x0000)]
      virtual void __cdecl CommandStateChange(long Command, VARIANT_BOOL Enable){} // 2
//        [id(DISPID_DOWNLOADBEGIN), helpstring("Download of a page started."), helpcontext(0x000)]
      virtual void __cdecl DownloadBegin(){} // 3
//        [id(DISPID_DOWNLOADCOMPLETE), helpstring("Download of page complete."), helpcontext(0x0000)]
      virtual void __cdecl DownloadComplete(){} // 4
//        [id(DISPID_TITLECHANGE), helpstring("Document title changed."), helpcontext(0x0000)]
      virtual void __cdecl TitleChange(BSTR Text){} // 5
//        [id(DISPID_PROPERTYCHANGE), helpstring("Fired when the PutProperty method has been called."), helpcontext(0x0000)]
      virtual void __cdecl PropertyChange(BSTR szProperty){} // 6
        // New events for IE40:
//        [id(DISPID_BEFORENAVIGATE2), helpstring("Fired before navigate occurs in the given WebBrowser (window or frameset element). The processing of this navigation may be modified."), helpcontext(0x0000)]
      virtual void __cdecl BeforeNavigate2(IDispatch* pDisp,VARIANT * URL,VARIANT * Flags,VARIANT * TargetFrameName,VARIANT * PostData,VARIANT * Headers,VARIANT_BOOL * Cancel){} // 7
//        [id(DISPID_NEWWINDOW2), helpstring("A new, hidden, non-navigated WebBrowser window is needed."), helpcontext(0x0000)]
      virtual void __cdecl NewWindow2(IDispatch** ppDisp, VARIANT_BOOL * Cancel){} // 8
//        [id(DISPID_NAVIGATECOMPLETE2), helpstring("Fired when the document being navigated to becomes visible and enters the navigation stack."), helpcontext(0x0000)]
      virtual void __cdecl NavigateComplete2(IDispatch* pDisp, VARIANT * URL ){} //9
//        [id(DISPID_DOCUMENTCOMPLETE), helpstring("Fired when the document being navigated to reaches ReadyState_Complete."), helpcontext(0x0000)]
      virtual void __cdecl DocumentComplete(IDispatch* pDisp, VARIANT * URL ){} //10
//        [id(DISPID_ONQUIT), helpstring("Fired when application is quiting."), helpcontext(0x0000)]
      virtual void __cdecl OnQuit(){} // 11
//        [id(DISPID_ONVISIBLE), helpstring("Fired when the window should be shown/hidden"), helpcontext(0x0000)]
      virtual void __cdecl OnVisible(VARIANT_BOOL Visible){} // 12
//        [id(DISPID_ONTOOLBAR), helpstring("Fired when the toolbar  should be shown/hidden"), helpcontext(0x0000)]
      virtual void __cdecl OnToolBar(VARIANT_BOOL ToolBar){} //13
//        [id(DISPID_ONMENUBAR), helpstring("Fired when the menubar should be shown/hidden"), helpcontext(0x0000)]
      virtual void __cdecl OnMenuBar(VARIANT_BOOL MenuBar){} // 14
//        [id(DISPID_ONSTATUSBAR), helpstring("Fired when the statusbar should be shown/hidden"), helpcontext(0x0000)]
      virtual void __cdecl OnStatusBar(VARIANT_BOOL StatusBar){} //15
//        [id(DISPID_ONFULLSCREEN), helpstring("Fired when fullscreen mode should be on/off"), helpcontext(0x0000)]
      virtual void __cdecl OnFullScreen(VARIANT_BOOL FullScreen){} //16
//        [id(DISPID_ONTHEATERMODE), helpstring("Fired when theater mode should be on/off"), helpcontext(0x0000)]
      virtual void __cdecl OnTheaterMode(VARIANT_BOOL TheaterMode){}// 17
        // New events for IE 5.5
//        [id(DISPID_WINDOWSETRESIZABLE), helpstring("Fired when the host window should allow/disallow resizing"), helpcontext(0x0000)]
      virtual void __cdecl WindowSetResizable(VARIANT_BOOL Resizable){} // 18
//        [id(DISPID_WINDOWSETLEFT), helpstring("Fired when the host window should change its Left coordinate"), helpcontext(0x0000)]
      virtual void __cdecl WindowSetLeft(long Left){} // 19
//        [id(DISPID_WINDOWSETTOP), helpstring("Fired when the host window should change its Top coordinate"), helpcontext(0x0000)]
      virtual void __cdecl WindowSetTop(long Top){} // 20
//        [id(DISPID_WINDOWSETWIDTH), helpstring("Fired when the host window should change its width"), helpcontext(0x0000)]
      virtual void __cdecl WindowSetWidth(long Width){} // 21
//        [id(DISPID_WINDOWSETHEIGHT), helpstring("Fired when the host window should change its height"), helpcontext(0x0000)]
      virtual void __cdecl WindowSetHeight(long Height){} // 22
//        [id(DISPID_WINDOWCLOSING), helpstring("Fired when the WebBrowser is about to be closed by script"), helpcontext(0x0000)]
      virtual void __cdecl WindowClosing(VARIANT_BOOL IsChildWindow, VARIANT_BOOL * Cancel){} //23
//        [id(DISPID_CLIENTTOHOSTWINDOW), helpstring("Fired to request client sizes be converted to host window sizes"), helpcontext(0x0000)]
      virtual void __cdecl ClientToHostWindow(long* CX, long* CY){} //24
//        [id(DISPID_SETSECURELOCKICON), helpstring("Fired to indicate the security level of the current web page contents"), helpcontext(0x0000)]
      virtual void __cdecl SetSecureLockIcon(long SecureLockIcon){} //25
//        [id(DISPID_FILEDOWNLOAD), helpstring("Fired to indicate the File Download dialog is opening"), helpcontext(0x0000)]
      virtual void __cdecl FileDownload(VARIANT_BOOL ActiveDocument, VARIANT_BOOL * Cancel){} //26
        // New events for IE 6.0
//        [id(DISPID_NAVIGATEERROR), helpstring("Fired when a binding error occurs (window or frameset element)."), helpcontext(0x0000)]
      virtual void __cdecl NavigateError(IDispatch* pDisp, VARIANT* URL, VARIANT* Frame, VARIANT* StatusCode, VARIANT_BOOL* Cancel){}
//        [id(DISPID_PRINTTEMPLATEINSTANTIATION), helpstring("Fired when a print template is instantiated."), helpcontext(0x0000)]
      virtual void __cdecl PrintTemplateInstantiation(IDispatch* pDisp){}
//        [id(DISPID_PRINTTEMPLATETEARDOWN), helpstring("Fired when a print template destroyed."), helpcontext(0x0000)]
      virtual void __cdecl PrintTemplateTeardown(IDispatch* pDisp){}
//        [id(DISPID_UPDATEPAGESTATUS), helpstring("Fired when a page is spooled. When it is fired can be changed by a custom template."), helpcontext(0x0000)]
      virtual void __cdecl UpdatePageStatus(IDispatch* pDisp, VARIANT* nPage, VARIANT* fDone){}
//        [id(DISPID_PRIVACYIMPACTEDSTATECHANGE), helpstring("Fired when the global privacy impacted state changes"), helpcontext(0x0000)]
      virtual void __cdecl PrivacyImpactedStateChange(VARIANT_BOOL bImpacted){}
//        [id(DISPID_NEWWINDOW3), helpstring("A new, hidden, non-navigated WebBrowser window is needed."), helpcontext(0x0000)]
      virtual void __cdecl NewWindow3(IDispatch** ppDisp, VARIANT_BOOL * Cancel, DWORD dwFlags, BSTR bstrUrlContext, BSTR bstrUrl){}
        // New events for IE 7.0
//        [id(DISPID_SETPHISHINGFILTERSTATUS), helpstring("Fired to indicate the progress and status of the Phishing Filter analysis of the current web page"), helpcontext(0x0000)]
      virtual void __cdecl SetPhishingFilterStatus(long PhishingFilterStatus){}
//        [id(DISPID_WINDOWSTATECHANGED), helpstring("Fired to indicate that the browser window's visibility or enabled state has changed."), helpcontext(0x0000)]
      virtual void __cdecl WindowStateChanged(DWORD dwWindowStateFlags, DWORD dwValidFlagsMask){}
        // New Events for IE 8.0
 //       [id(DISPID_NEWPROCESS), helpstring("A new, hidden, non-navigated process is created to handle the navigation."), helpcontext(0x0000)]
       virtual void __cdecl NewProcess(long lCauseFlag, IDispatch* pWB2,VARIANT_BOOL * Cancel){}
//        [id(DISPID_THIRDPARTYURLBLOCKED), helpstring("Fired when a third-party URL is blocked."), helpcontext(0x0000)]
       virtual void __cdecl ThirdPartyUrlBlocked(VARIANT* URL, DWORD dwCount){}
//        [id(DISPID_REDIRECTXDOMAINBLOCKED), helpstring("Fired when a x-domain redirect is blocked."), helpcontext(0x0000)]
      virtual void __cdecl RedirectXDomainBlocked(IDispatch* pDisp, VARIANT* StartURL, VARIANT* RedirectURL, VARIANT* Frame, VARIANT* StatusCode){}
    };

    class WebBrowserEventsDelegateDispatcher : public StandardDispatch
    {
    };


    class WebBrowserSite : public ActiveXSite, public IDocHostUIHandler2
    {
      typedef class ActiveXSite super;
    protected:
      ComPtr<IWebBrowser2> _iWebBrowser2;
      DWORD _cookie;
    public: // WebBrowserSite
      WebBrowserSite();
      ~WebBrowserSite();
      void init(IOleInPlaceFrame* frame,WebBrowserEventsDelegate*delegate);
      virtual void close() override;
      void loadRequest(string url);

    public: // IUnknown
      virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void **ppvObject) override;
      virtual ULONG STDMETHODCALLTYPE AddRef( void)=0;
      virtual ULONG STDMETHODCALLTYPE Release( void)=0;
    public: // IDispatch
        //virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) override;
        //virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo **ppTInfo) override;
        //virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid,LPOLESTR *rgszNames,UINT cNames,LCID lcid,DISPID *rgDispId) override;
        //virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr) override;
    public: // IDocHostUIHandler
      virtual HRESULT STDMETHODCALLTYPE ShowContextMenu(DWORD dwID,POINT *ppt,IUnknown *pcmdtReserved,IDispatch *pdispReserved)override;
      virtual HRESULT STDMETHODCALLTYPE GetHostInfo(DOCHOSTUIINFO *pInfo)override;
      virtual HRESULT STDMETHODCALLTYPE ShowUI(DWORD dwID,IOleInPlaceActiveObject *pActiveObject,IOleCommandTarget *pCommandTarget,IOleInPlaceFrame *pFrame,IOleInPlaceUIWindow *pDoc)override;
      virtual HRESULT STDMETHODCALLTYPE HideUI( void)override;
      virtual HRESULT STDMETHODCALLTYPE UpdateUI( void)override;
      virtual HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable)override;
      virtual HRESULT STDMETHODCALLTYPE OnDocWindowActivate(BOOL fActivate)override;
      virtual HRESULT STDMETHODCALLTYPE OnFrameWindowActivate(BOOL fActivate)override;
      virtual HRESULT STDMETHODCALLTYPE ResizeBorder(LPCRECT prcBorder,IOleInPlaceUIWindow *pUIWindow,BOOL fRameWindow)override;
      virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpMsg,const GUID *pguidCmdGroup,DWORD nCmdID)override;
      virtual HRESULT STDMETHODCALLTYPE GetOptionKeyPath(LPOLESTR *pchKey,DWORD dw)override;
      virtual HRESULT STDMETHODCALLTYPE GetDropTarget(IDropTarget *pDropTarget,IDropTarget **ppDropTarget)override;
      virtual HRESULT STDMETHODCALLTYPE GetExternal(IDispatch **ppDispatch)override;
      virtual HRESULT STDMETHODCALLTYPE TranslateUrl(DWORD dwTranslate,OLECHAR *pchURLIn,OLECHAR **ppchURLOut)override;
      virtual HRESULT STDMETHODCALLTYPE FilterDataObject(IDataObject *pDO,IDataObject **ppDORet)override;
    public: // IDocHostUIHandler2
      virtual HRESULT STDMETHODCALLTYPE GetOverrideKeyPath(LPOLESTR *pchKey,DWORD dw) override;
    };


// It is very difficult to mix ActiveX objects with RAII classes as COM objects tend to aquire
// outgoing interface references on their host objects causing circular references.
    class WebView :
      public WebBrowserSite
    {
      typedef class WebBrowserSite super;
    protected:
      ComPtr<IDispatch> _external;
    public: 
      WebView();
      ~WebView();
      void initWithFrame(Rect frame,IOleInPlaceFrame* asContentForWindow,WebBrowserEventsDelegate*delegate,void* externalClass, INTERFACEDATA* externalData);
//      void loadRequest(LPCTSTR url);
    public: // IOleWindow
      virtual HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd)override;
      virtual HRESULT STDMETHODCALLTYPE GetExternal(IDispatch **ppDispatch)override;
    };


  }
}
